package com.woniuxy.gateway.filter;

import cn.hutool.jwt.JWTException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.woniuxy.common.api.CommonResult;
import com.woniuxy.common.api.enums.ResultCode;
import com.woniuxy.common.jwt.util.JwtTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {

    public static final String TOKEN_HEADER = "Authorization";

    public static final String HEADER_BEARER = "Bearer ";
    private static final String REDIS_KEY = "auth:user:";
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private JwtTemplate jwtTemplate;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String path = request.getURI().getPath();
        AntPathMatcher matcher = new AntPathMatcher();
        if (matcher.match("/**/auth/**", path)) {
            //获取请求头中的token
            String headerToken = request.getHeaders().getFirst(TOKEN_HEADER);
            if (StringUtils.isBlank(headerToken)) {
                log.info("请求头中token不存在");
                //响应错误消息
                return responseError(response);
            }
            String token = headerToken.replace(HEADER_BEARER, "");
            //验证token
            try {
                boolean verify = jwtTemplate.verify(token);
                if (!verify) {
                    log.info("token验证失败");
                    return responseError(response);
                }
            } catch (JWTException e) {
                log.info("token格式错误");
                return responseError(response);
            }
            //获取载荷中的数据
            String userId = (String) jwtTemplate.getClaims(token, "userId");
            if (StringUtils.isBlank(userId)) {
                log.info("请求userId为空");
                return responseError(response);
            }
            //从redis中获取token
            String redisToken = (String) redisTemplate.opsForValue().get(REDIS_KEY + userId);
            if (Objects.isNull(redisToken)) {
                log.info("token过期失效");
                return responseError(response);
            }
            //重写请求头，将userID传递到后续的业务系统
            ServerHttpRequest.Builder builder = request.mutate();
            builder.header("userId", userId);
            //续期
            resetToken(redisToken, REDIS_KEY + userId);
        }

        return chain.filter(exchange);
    }

    private void resetToken(String redisToken, String redisKey) {
        redisTemplate.opsForValue().set(redisKey, redisToken, 30L, TimeUnit.MINUTES);
    }

    private Mono<Void> responseError(ServerHttpResponse response) {
        CommonResult result = CommonResult.failure(ResultCode.PERMISSION_NO_ACCESS);
        try {
            //将结果对象转换为字节数组
            byte[] data = objectMapper.writer().writeValueAsBytes(result);
            //将字节数组写入到缓冲区
            DataBuffer dataBuffer = response.bufferFactory().wrap(data);
            //响应数据
            return response.writeWith(Mono.just(dataBuffer));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 数字越小，优先级越高
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
